終於要來開始實作存取「裝置的媒體」和「地理位置」的功能。
當用戶要發佈貼文時,應該要開啟裝置的攝像鏡頭並將畫面顯示在PWA的表單中,當用戶按下拍照鍵時即完成影像拍攝,另外也會有一個按鈕來獲取裝置所在的地理位置。
首先,在index.html中的表單加入上面說到的button以及video元素吧:
<video id="player" autoplay></video>
<canvas id="canvas" width="320px", height="240px"></canvas>
<button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored" id="capture-btn">拍下屬於你的時刻</button>
<div id="pick-image">
<h6>選擇圖片</h6>
<input type="file" accept="image/*" id="image-picker">
</div>
<div class="input-section">
<button class="mdl-button mdl-js-button mdl-button mdl-button-colored" type="button" id="location-btn">目前所在地</button>
<div class="mdl-spinner mdl-js-spinner is-active" id="location-loader"></div>
</div>
除了用video元素來顯示串流影像之外,這邊為了要實現拍照的功能,當用戶按下拍照鍵後,就會擷取當下影像中的frame並放置在canvas中。
但如果用戶裝置沒有攝像鏡頭或是browser不支援的話,就改為自行上傳圖片來代替(image uploader)。最後加入要獲取地理位置的button,以及等待時的loading圖示。
接下來在feed.js修改一下新增的element大小和位置,並預設將file uploader隱藏起來:
#create-post {
z-index: 1001;
position: fixed;
width: 100%;
min-height: calc(100vh - 56px);
overflow-y: scroll;
bottom: 0;
top: 56px;
background: white;
text-align: center;
transform: translateY(100vh);
transition: transform 0.3s;
}
#create-post video, #create-post canvas {
width: 512px;
max-width: 100%;
display: none;
margin: auto;
}
#create-post #pick-image {
display: none;
}
#create-post #capture-btn {
margin: 10px auto;
}
.mdl-spinner {
margin: auto;
}
結果如下:
接下來,在feed.js中存取剛剛添加的element,並針對這些element開始實作拍照的功能吧:
var videoPlayer = document.querySelector('#player');
var canvasElement = document.querySelector('#canvas');
var captureButton = document.querySelector('#capture-btn');
var imagePicker = document.querySelector('#image-picker');
var imagePickerArea = document.querySelector('#pick-image');
在openCreatePostModal() function中新增「初始化媒體(initializeMedia)」的函式。希望用戶在開啟表單前,先確認瀏覽器是否有支援「存取媒體(getUserMedia)」這個API。
所以為了在舊的browser中使用getUserMedia() API,必須帶一個polyfill以適應舊browser:(參考連結)
function initializeMedia() {
// 舊browser可能根本沒有實現mediaDevices,所以可以先設置一個empty object
if(!('mediaDevices' in navigator)) {
navigator.mediaDevices = {};
}
// 一些browser只有部分支持mediaDevices。所以不能直接給在該object設置getUserMedia
// 因為這樣可能會覆蓋已有的屬性。這裡我們只會在沒有 getUserMedia 屬性的時候添加它。
if(!('getUserMedia' in navigator.mediaDevices)) {
// getUserMedia的constraints參數包含了video和audio兩個成員的MediaStreamConstraints object
navigator.mediaDevices.getUserMedia = function(constraints) {
// 首先,如果有getUserMedia的話,就取得它
var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// 一些browser根本沒實現它 - 那麼就返回一個error到promise的reject
if(!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented!'));
}
// 否則,為原有的navigator.getUserMedia()方法包裹一個Promise
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
// getUserMedia會返回一個Promise,這個Promise成功後的callback帶一個MediaStream object作為參數。
navigator.mediaDevices.getUserMedia({video: true}).then(function(stream) {
// 顯示video element
videoPlayer.srcObject = stream;
videoPlayer.style.display = 'block';
}).catch(function(err) {
// 若getUserMedia返回error,就讓用戶自行上傳圖片(image uploader)來代替
imagePickerArea.style.display = 'block';
});
}
明天再來實作當用戶按下拍照鍵後,就將當下影像的frame顯示在canvas上,並將這張圖片儲存至後台的firebase storage中。
Day25 結束!!